/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						       mass_spell.cpp

	$Header: /game/mass_spell.cpp $

	$NoKeywords: $

 ************************************************************************/

// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "mass_spell.h"

#include <vector>
#include "abstract_combat_ai_spell_action.h"
#include "adaptor_handler.h"
#include "battlefield.h"
#include "bound_handler.h"
#include "can_cast.h"
#include "combat_ai.h"
#include "combat_creature.h"
#include "combat_spell_registration.h"
#include "elements_of.h"
#include "enum_operations.h"
#include "external_string.h"
#include "format_string.h"
#include "missile_type.h"
#include "pixel_24.h"
#include "replace_keywords.h"
#include "simple_dialog.h"
#include "spell.h"
#include "spell_properties.h"

namespace
{
	struct t_spell_pair
	{
		t_spell spell;
		t_spell base_spell;
	};

	// ----------------------------------------------------------------
	// list of mass spells and the corresponding base spells
	// ----------------------------------------------------------------
	t_spell_pair const k_mass_spells[] =
	{
		{ k_spell_bloodfrenzy,					k_spell_bloodlust },
		{ k_spell_divine_aura,					k_spell_holy_armor },
		{ k_spell_divine_retribution,			k_spell_holy_word },
		{ k_spell_greater_forgetfulness,		k_spell_forgetfulness },
		{ k_spell_mass_bless,					k_spell_bless },
		{ k_spell_mass_blur,					k_spell_blur },
		{ k_spell_mass_cancellation,			k_spell_cancellation },
		{ k_spell_mass_curse,					k_spell_curse },
		{ k_spell_mass_dispel,					k_spell_dispel },
		{ k_spell_mass_exorcism,				k_spell_exorcism },
		{ k_spell_mass_first_strike,			k_spell_first_strike },
		{ k_spell_mass_fortune,					k_spell_fortune },
		{ k_spell_mass_haste,					k_spell_nature_haste },
		{ k_spell_mass_healing,					k_spell_heal },
		{ k_spell_mass_misfortune,				k_spell_misfortune },
		{ k_spell_mass_precision,				k_spell_precision },
		{ k_spell_mass_protection_from_chaos,	k_spell_protection_from_chaos },
		{ k_spell_mass_protection_from_death,	k_spell_protection_from_death },
		{ k_spell_mass_protection_from_nature,	k_spell_protection_from_nature },
		{ k_spell_mass_protection_from_order,	k_spell_protection_from_order },
		{ k_spell_mass_slayer,					k_spell_slayer },
		{ k_spell_mass_slow,					k_spell_order_slow },
		{ k_spell_mass_snake_strike,			k_spell_snake_strike },
		{ k_spell_mass_sorrow,					k_spell_sorrow },
		{ k_spell_mass_spiritual_fervor,		k_spell_spiritual_fervor },
		{ k_spell_mass_weakness,				k_spell_weakness },
	};

	// ----------------------------------------------------------------
	// factory to create a mass spell
	// ----------------------------------------------------------------
	class t_mass_spell_factory : public t_spell_factory_base
	{
	public:
		t_mass_spell_factory( t_spell base_spell );
		
		virtual t_combat_spell* create( t_battlefield& battlefield, t_spell spell ) const;
	protected:
		t_spell m_base_spell;
	};

	inline t_mass_spell_factory::t_mass_spell_factory( t_spell base_spell )
	{
		m_base_spell = base_spell;
	}

	// ----------------------------------------------------------------
	// registeration class for mass spells
	// ----------------------------------------------------------------
	class t_mass_spell_register : public t_combat_spell_registration_base
	{
	public:
		t_mass_spell_register( t_spell_pair const* spells, int count );
	protected:
		std::vector<t_mass_spell_factory> m_factories;
	};

	t_mass_spell_register k_mass_registration( k_mass_spells, ELEMENTS_OF( k_mass_spells ));
};

// ----------------------------------------------------------------
// factory to create a mass spell
// ----------------------------------------------------------------
t_combat_spell* t_mass_spell_factory::create( t_battlefield& battlefield, t_spell spell ) const
{
	return new t_mass_spell( battlefield, spell, m_base_spell );
}

// ----------------------------------------------------------------
// registeration class for mass spells
// ----------------------------------------------------------------
t_mass_spell_register::t_mass_spell_register( t_spell_pair const* spells, int count )
{
	int i;

	m_factories.reserve( count );
	for (i = 0; i < count; i++)
	{
		m_factories.push_back( t_mass_spell_factory( spells[i].base_spell ) );
	}
	for (i = 0; i < count; i++)
	{
		register_spell( spells[i].spell, &m_factories[i] );
	}
}

// ----------------------------------------------------------------
// spell with multiple targets
// ----------------------------------------------------------------
t_mass_spell_base::t_mass_spell_base( t_battlefield& battlefield, t_spell spell )
                 : t_combat_spell( battlefield, spell)
{
}

// ----------------------------------------------------------------
// spell with multiple targets
// ----------------------------------------------------------------
extern t_external_string const k_text_no_effect("spell_no_effect.spells" );

bool t_mass_spell_base::begin_casting()
{
	t_combat_creature_list::iterator index = m_battlefield.creatures_begin();
	t_combat_creature_list::iterator end   = m_battlefield.creatures_end();
	t_combat_spell_ptr               spell_object;
	std::string                      help_text;

	for (; index != end; index++)
	{
		if (spell_can_affect( *m_caster, **index ))
			break;
	}

	std::string text;

	if (index == end)
	{
		text = replace_keywords( k_text_no_effect, "%spell_name", get_spell_name( m_spell ));
		ok_dialog( text, true );
		return false;
	}
	m_battlefield.begin_spell( 0 );
	return true;
}

// ----------------------------------------------------------------
// spell with multiple targets
// ----------------------------------------------------------------
void t_mass_spell_base::execute( t_combat_creature& caster )
{
	t_counted_ptr<t_mass_spell_base> ref = this;

	pay_cost();
	m_battlefield.set_spell( 0 );

	std::string                      help_text;
	int								 power = m_caster->get_spell_power( m_spell );
	t_combat_creature_list::iterator index = m_battlefield.creatures_begin();
	t_combat_creature_list::iterator end   = m_battlefield.creatures_end();

	for (; index != end; index++)
	{
		if (!spell_can_affect( *m_caster, **index ))
			continue;
		cast_and_mirror( index->get(), power );
	}
	m_battlefield.update_state();
};

bool t_mass_spell_base::spell_can_affect( t_combat_creature const& caster,
										  t_combat_creature const& target ) const
{
	return can_affect( caster, m_spell, target );
}

// --------------------------------------------------------------------
// Generate the list of casting actions the given caster could take
// for this spell
// --------------------------------------------------------------------
t_combat_ai_action_list t_mass_spell_base::generate_combat_ai_action_list(t_combat_ai &owner)
{
	t_combat_ai_action_list				list;
	double								total_weight = 0.0;
	double								weight = 0.0;
	t_combat_creature&					caster		 = *get_caster();
	int									power		 = caster.get_spell_power( m_spell );
	t_battlefield&						battlefield = m_battlefield;
	t_combat_creature_list::iterator	index = battlefield.creatures_begin();
	t_combat_creature_list::iterator	end   = battlefield.creatures_end();
	bool								side = caster.get_controller();
	double								side_weight[2];
	t_combat_creature*					creature;

	side_weight[0] = 0.0;
	side_weight[1] = 0.0;
	for (; index != end; index++)
	{
		creature = *index;
		if (!spell_can_affect( caster, *creature ))
			continue;
		weight = ai_weight( *creature );
		total_weight += weight;
		side_weight[creature->belongs_to_defender()] += weight;
	}
	if (total_weight <= 0.0)
		return list;

	// consider how much benefit we're giving the enemy, vs. relative strength.
	t_ai_combat_data_cache&	data = battlefield.get_data_cache();

	// here, we're comparing (percentage friendly gain) to (percentage enemy gain),
	// but the equation is re-arranged to use multiplies to avoid any possible
	// divide by zero.
	if (side_weight[side] * data.get_total_combat_value( !side )
		<= -side_weight[!side] * data.get_total_combat_value( side ))
		return list;

	list.push_back( new t_combat_ai_spell_action( m_battlefield, this, total_weight ) );
	return list;
}

// ----------------------------------------------------------------
// spell with multiple targets
// ----------------------------------------------------------------
t_mass_spell::t_mass_spell( t_battlefield& battlefield, t_spell spell, t_spell base_spell )
            : t_mass_spell_base( battlefield, spell )
{
	m_base_spell = base_spell;
	m_spell_object = create_combat_spell( battlefield, base_spell );
	m_spell_object->set_cast_spell( spell ); // Get correct message
}

// ----------------------------------------------------------------
// spell with multiple targets
// ----------------------------------------------------------------
double t_mass_spell::ai_weight( t_combat_creature const& target ) const
{
	return m_spell_object->ai_weight( target );
}

double t_mass_spell::get_cancel_weight( t_combat_creature const& target ) const
{
	return m_spell_object->get_cancel_weight( target );
}

// ----------------------------------------------------------------
// spell with multiple targets
// ----------------------------------------------------------------
bool t_mass_spell::cast_on( t_combat_creature_ptr caster, t_combat_creature_ptr target,
						    int power, bool is_mirror )
{
	m_spell_object->cast_and_mirror( target, power );
	return true;
}

bool t_mass_spell::spell_can_affect( t_combat_creature const& caster, 
									 t_combat_creature const& target ) const
{
	return can_affect( caster, m_base_spell, target );
}
// ----------------------------------------------------------------
// divine intervention
// ----------------------------------------------------------------
namespace 
{
	class t_divine_intervention : public t_mass_spell_base
	{
	public:
		t_divine_intervention( t_battlefield& battlefield, t_spell spell );

		virtual double	ai_weight( t_combat_creature const& target ) const;
		virtual bool	cast_on( t_combat_creature_ptr caster,
								 t_combat_creature_ptr target, 
								 int power,
								 bool is_mirror );
		virtual double	get_cancel_weight( t_combat_creature const& target ) const;
	};
};

static t_combat_spell_registration<t_divine_intervention>
       k_intervention_register( k_spell_divine_intervention );

// ----------------------------------------------------------------
// divine intervention
// ----------------------------------------------------------------
t_divine_intervention::t_divine_intervention( t_battlefield& battlefield, t_spell spell )
                     : t_mass_spell_base( battlefield, spell )
{
}

// ----------------------------------------------------------------
// divine intervention
// ----------------------------------------------------------------
bool t_divine_intervention::cast_on( t_combat_creature_ptr caster,
									 t_combat_creature_ptr target, 
									 int power,
									 bool is_mirror )
{
	t_spell spell;
	t_combat_action_message message( *caster, get_action_text( is_mirror ) );

	m_battlefield.start_animation( *target, m_spell, message );
	for (spell = t_spell(0); spell < k_spell_count; enum_incr(spell))
		if (target->is_active( spell ) && is_curse( spell ))
			target->clear_spell( spell, message );
	if (target->get_wounds() > 0 || target->get_number() == 0 )
	{
		if (target->get_hero() == 0)
			target->heal( power );
		else
		{
			if (target->get_number() == 0)
				target->show_resurrection( message );
			target->resurrect( power );
		}
	}
	return true;
}

// ----------------------------------------------------------------
// divine intervention
// ----------------------------------------------------------------
double t_divine_intervention::ai_weight( t_combat_creature const& target ) const
{
	t_battlefield&			battlefield = m_battlefield;
	t_ai_combat_data_cache&	data = battlefield.get_data_cache();
	int						losses = data.get_approximate_losses( target );

	if (losses < 1)
		return 0.0;

	int healing = m_caster->get_spell_power(m_spell);
	int wounds = target.get_wounds();

	if (target.get_number() == 0)
		wounds += target.get_hit_points();
	else if (target.has_ability( k_ability_regeneration ))
		wounds = 0;
	else if (target.is_active( k_spell_regeneration ))
	{
		wounds -= target.get_effect_power( k_spell_regeneration );
		if (wounds < 0)
			wounds = 0;
	}
	if (healing > wounds)
		healing = target.get_wounds();

	double result = healing * target.get_ai_value_per_hit();

	t_spell_list	cancellation_list;
	t_spell			spell;

	for (spell = t_spell(0); spell < k_spell_count; enum_incr(spell))
		if (target.is_active( spell ) && is_curse( spell ))
			cancellation_list.push_back( spell );
	return result + get_cancel_value( m_battlefield, target, cancellation_list );
}

double t_divine_intervention::get_cancel_weight( t_combat_creature const& target ) const
{
	return 0.0;
}

// ----------------------------------------------------------------
// divine protection
// ----------------------------------------------------------------
namespace
{
	class t_mass_only_spell : public t_mass_spell_base
	{
	public:
		t_mass_only_spell( t_battlefield& battlefield, t_spell spell );

		virtual bool cast_on( t_combat_creature_ptr caster,
			                  t_combat_creature_ptr target,
							  int power,
							  bool is_mirror );
	};
};


// ----------------------------------------------------------------
// divine protection
// ----------------------------------------------------------------
t_mass_only_spell::t_mass_only_spell( t_battlefield& battlefield, t_spell spell )
                   : t_mass_spell_base( battlefield, spell )
{
}

// ----------------------------------------------------------------
// divine protection
// ----------------------------------------------------------------
bool t_mass_only_spell::cast_on( t_combat_creature_ptr caster,
								 t_combat_creature_ptr target, 
								 int power,
								 bool is_mirror )
{
	t_combat_action_message message( *caster, get_action_text( is_mirror ) );
	return m_battlefield.cast_spell( *caster, *target, m_spell, message, power );
}

// ----------------------------------------------------------------
// divine protection
// ----------------------------------------------------------------
namespace
{
	class t_spell_divine_protection : public t_mass_only_spell
	{
	public:
		t_spell_divine_protection( t_battlefield& battlefield, t_spell spell );

		virtual double	ai_weight( t_combat_creature const& target ) const;
		virtual double	get_cancel_weight( t_combat_creature const& target ) const;
	};

	inline t_spell_divine_protection::t_spell_divine_protection( t_battlefield& battlefield, t_spell spell )
									: t_mass_only_spell( battlefield, spell )
	{
	}

	t_combat_spell_registration<t_spell_divine_protection> const
		k_register_divine_protection( k_spell_divine_protection );
};

double t_spell_divine_protection::ai_weight( t_combat_creature const& target ) const
{
	return 0.0;
}

double t_spell_divine_protection::get_cancel_weight( t_combat_creature const& target ) const
{
	double result = ai_weight( target );

	if (target.belongs_to_defender() == m_caster->get_controller())
		result = -result;
	return result;
}

// -----------------------------------------------------------------
// sanctuary spell
// -----------------------------------------------------------------
namespace
{
	class t_spell_sanctuary : public t_mass_only_spell
	{
	public:
		t_spell_sanctuary( t_battlefield& battlefield, t_spell spell );

		virtual double  ai_weight( t_combat_creature const& target ) const;
		virtual double	get_cancel_weight( t_combat_creature const& target ) const;
	};
	// -----------------------------------------------------------------
	// sanctuary spell
	// -----------------------------------------------------------------
	inline t_spell_sanctuary::t_spell_sanctuary( t_battlefield& battlefield, t_spell spell )
					  : t_mass_only_spell( battlefield, spell )
	{
	}

	static t_combat_spell_registration<t_spell_sanctuary> const
		   k_sanctuary_registration( k_spell_sanctuary );


	// -----------------------------------------------------------------
	// sanctuary spell
	// -----------------------------------------------------------------
	double t_spell_sanctuary::ai_weight( t_combat_creature const& target ) const
	{
		// Not implemented
		return 0.0;
	}

	double t_spell_sanctuary::get_cancel_weight( t_combat_creature const& target ) const
	{
		double result = ai_weight( target );

		if (target.belongs_to_defender() == m_caster->get_controller())
			result = -result;
		return result;
	}

} // End anonymous namespace


// ----------------------------------------------------------------
// evil hour
// ----------------------------------------------------------------
namespace
{
	class t_spell_affects_all : public t_mass_only_spell
	{
	public:
		t_spell_affects_all( t_battlefield& battlefield, t_spell spell );

		virtual double	ai_weight( t_combat_creature const& target ) const;
		virtual double	get_cancel_weight( t_combat_creature const& target ) const;
		virtual double	get_value( t_combat_creature const& target ) const = 0;
	};

	inline t_spell_affects_all::t_spell_affects_all( t_battlefield& battlefield, t_spell spell )
							  : t_mass_only_spell( battlefield, spell )
	{
	}

	class t_spell_evil_hour : public t_spell_affects_all
	{
	public:
		t_spell_evil_hour( t_battlefield& battlefield, t_spell spell );

		virtual double	get_value( t_combat_creature const& target ) const;
	};

	inline t_spell_evil_hour::t_spell_evil_hour( t_battlefield& battlefield, t_spell spell )
									: t_spell_affects_all( battlefield, spell )
	{
	}

	t_combat_spell_registration<t_spell_evil_hour> const
		k_register_evil_hour( k_spell_evil_hour );
};

double t_spell_affects_all::ai_weight( t_combat_creature const& target ) const
{
	double result = get_value( target );
	
	if (target.belongs_to_defender() != m_caster->get_controller())
		result = -result;
	if (can_resist( m_spell ))
		result = result * m_caster->get_spell_chance( target, m_spell ) * 0.01;
	return result;
}

double t_spell_affects_all::get_cancel_weight( t_combat_creature const& target ) const
{
	double result = get_value( target );

	if (target.belongs_to_defender() == m_caster->get_controller())
		result = -result;
	return result;
}

double t_spell_evil_hour::get_value( t_combat_creature const& target ) const
{
	double result;
	double damage = (target.get_damage_low() + target.get_damage_high()) * 0.5 * 0.2;

	if (!spellcasting_is_worthwhile( m_battlefield, false ) || !spellcasting_is_worthwhile( m_battlefield, true ))
		return 0.0;
	if (get_approximate_lifespan( m_battlefield, target ) < 2)
		return 0.0;
	result = -target.get_damage_decrease_ai_value( damage );
	result -= target.get_defense_decrease_ai_value( 0.25, true )
			+ target.get_damage_decrease_ai_value( 0.25, false );

	return result;
}

// ----------------------------------------------------------------
// prayer
// ----------------------------------------------------------------
namespace
{
	class t_spell_prayer : public t_spell_affects_all
	{
	public:
		t_spell_prayer( t_battlefield& battlefield, t_spell spell );

		virtual double	get_value( t_combat_creature const& target ) const;
	};

	inline t_spell_prayer::t_spell_prayer( t_battlefield& battlefield, t_spell spell )
									: t_spell_affects_all( battlefield, spell )
	{
	}

	t_combat_spell_registration<t_spell_prayer> const
		k_register_prayer( k_spell_prayer );
};

double t_spell_prayer::get_value( t_combat_creature const& target ) const
{
	double damage = (target.get_damage_low() + target.get_damage_high()) * 0.5 * 0.25;

	if (!spellcasting_is_worthwhile( m_battlefield, false ) || !spellcasting_is_worthwhile( m_battlefield, true ))
		return 0.0;
	if (get_approximate_lifespan( m_battlefield, target ) < 2)
		return 0.0;
	return target.get_damage_increase_ai_value( damage )
		 + target.get_defense_increase_ai_value( 0.25, true )
		 + target.get_defense_increase_ai_value( 0.25, false );
}

// ----------------------------------------------------------------
// plague
// ----------------------------------------------------------------
namespace
{
	class t_spell_plague : public t_mass_only_spell
	{
	public:
		t_spell_plague( t_battlefield& battlefield, t_spell spell );

		virtual double	ai_weight( t_combat_creature const& target ) const;
		virtual bool	cast_on( t_combat_creature_ptr caster, 
		                             t_combat_creature_ptr target, 
									 int power,
									 bool is_mirror );
		virtual double	get_cancel_weight( t_combat_creature const& target ) const;


	};

	inline t_spell_plague::t_spell_plague( t_battlefield& battlefield, t_spell spell )
						 : t_mass_only_spell( battlefield, spell )
	{
	}

	t_combat_spell_registration<t_spell_plague> const
		k_register_plague( k_spell_plague );
};

double t_spell_plague::ai_weight( t_combat_creature const& target ) const
{
	int power = m_caster->get_spell_power( m_spell );

	if (!spellcasting_is_worthwhile( m_battlefield, m_caster->get_controller() ))
		return 0.0;
	power = target.modify_spell_damage( m_caster, power, m_spell );
	power -= target.get_effect_power( m_spell );
	if (power <= 0)
		return 0.0;

	double result = get_poison_value( target, power );

	if (target.belongs_to_defender() == m_caster->get_controller())
		result = -result;
	return result;
}

bool t_spell_plague::cast_on( t_combat_creature_ptr caster, t_combat_creature_ptr target,
							    int power, bool is_mirror )
{
	int damage;

	t_combat_action_message message( *caster, get_action_text( is_mirror ) );

	damage = target->modify_spell_damage( caster, power, m_spell );
	target->set_spell( m_spell, message, damage );
	m_battlefield.start_animation( *target, m_spell, message );
	return true;
}

double t_spell_plague::get_cancel_weight( t_combat_creature const& target ) const
{
	double result = get_poison_value( target, target.get_effect_power( m_spell ));

	if (target.belongs_to_defender() != m_caster->get_controller())
		result = -result;
	return result;
}

// ----------------------------------------------------------------
// mana flare
// ----------------------------------------------------------------
namespace
{
	class t_spell_mana_flare : public t_mass_only_spell
	{
	public:
		t_spell_mana_flare( t_battlefield& battlefield, t_spell spell );

		virtual double	ai_weight( t_combat_creature const& target ) const;
		virtual double	get_cancel_weight( t_combat_creature const& target ) const;
	};

	inline t_spell_mana_flare::t_spell_mana_flare( t_battlefield& battlefield, t_spell spell )
									: t_mass_only_spell( battlefield, spell )
	{
	}

	t_combat_spell_registration<t_spell_mana_flare> const
		k_register_mana_flare( k_spell_mana_flare );
};

double t_spell_mana_flare::ai_weight( t_combat_creature const& target ) const
{
	double spellcasting_value = target.get_ai_spellcasting_value_per_hit();
	double melee_value	= target.get_ai_melee_value_per_hit();
	double ranged_value = target.get_ai_ranged_value_per_hit();

	if (!spellcasting_is_worthwhile( m_battlefield, false ) || !spellcasting_is_worthwhile( m_battlefield, true ))
		return 0.0;
	if (get_approximate_lifespan( m_battlefield, target ) < 2)
		return 0.0;
	if (melee_value < ranged_value)
		melee_value = ranged_value;
	if (spellcasting_value < melee_value)
		return 0.0;

	t_battlefield&			battlefield = m_battlefield;
	t_ai_combat_data_cache&	data = battlefield.get_data_cache();
	int						lifetime = data.get_approximate_lifespan( target );
	int						limit = data.get_approximate_battle_length();
	int						spell_cost;
	int						spell_points;
	int						old_spell_turns;
	int						new_spell_turns;

	if (lifetime > limit)
		lifetime = limit;
	spell_cost = get_spell_cost_per_turn( target );
	if (spell_cost < 2)
		return 0.0;
	spell_points = target.get_spell_points();
	old_spell_turns = spell_points / spell_cost;
	if (old_spell_turns < 1)
		old_spell_turns = 1;
	spell_cost -= 2;
	if (spell_cost < 1)
		spell_cost = 1;
	new_spell_turns = spell_points / spell_cost;
	if (old_spell_turns > lifetime)
		old_spell_turns = lifetime;
	if (new_spell_turns > lifetime)
		new_spell_turns = lifetime;
	if (new_spell_turns <= old_spell_turns)
		return 0.0;
	spellcasting_value -= melee_value;
	spellcasting_value *= target.get_total_hits();
	spellcasting_value = spellcasting_value * (new_spell_turns - old_spell_turns) / old_spell_turns;
	if (target.belongs_to_defender() != m_caster->get_controller())
		spellcasting_value = -spellcasting_value;
	return spellcasting_value;
}

double t_spell_mana_flare::get_cancel_weight( t_combat_creature const& target ) const
{
	double spellcasting_value = target.get_ai_spellcasting_value_per_hit();
	double melee_value	= target.get_ai_melee_value_per_hit();
	double ranged_value = target.get_ai_ranged_value_per_hit();

	if (!spellcasting_is_worthwhile( m_battlefield, false ) || !spellcasting_is_worthwhile( m_battlefield, true ))
		return 0.0;
	if (get_approximate_lifespan( m_battlefield, target ) < 2)
		return 0.0;
	if (melee_value < ranged_value)
		melee_value = ranged_value;
	if (spellcasting_value < melee_value)
		return 0.0;

	t_battlefield&			battlefield = m_battlefield;
	t_ai_combat_data_cache&	data = battlefield.get_data_cache();
	int						lifetime = data.get_approximate_lifespan( target );
	int						limit = data.get_approximate_battle_length();
	int						spell_cost;
	int						spell_points;
	int						old_spell_turns;
	int						new_spell_turns;

	if (lifetime > limit)
		lifetime = limit;
	spell_cost = get_spell_cost_per_turn( target );
	if (spell_cost < 1)
		return 0.0;
	spell_points = target.get_spell_points();
	old_spell_turns = spell_points / spell_cost;
	if (old_spell_turns < 1)
		old_spell_turns = 1;
	spell_cost += 2;
	new_spell_turns = spell_points / spell_cost;
	if (old_spell_turns > lifetime)
		old_spell_turns = lifetime;
	if (new_spell_turns > lifetime)
		new_spell_turns = lifetime;
	if (new_spell_turns >= old_spell_turns)
		return 0.0;
	spellcasting_value -= melee_value;
	spellcasting_value *= target.get_total_hits();
	spellcasting_value = spellcasting_value * (old_spell_turns - new_spell_turns) / old_spell_turns;
	if (target.belongs_to_defender() == m_caster->get_controller())
		spellcasting_value = -spellcasting_value;
	return spellcasting_value;
}

// ----------------------------------------------------------------
// Necromancy Ward
// ----------------------------------------------------------------
namespace
{
	class t_spell_necromancy_ward : public t_mass_only_spell
	{
	public:
		t_spell_necromancy_ward( t_battlefield& battlefield, t_spell spell );

		virtual double	ai_weight( t_combat_creature const& target ) const;
		virtual double	get_cancel_weight( t_combat_creature const& target ) const;
	private:
		double get_spell_protection( t_combat_creature const& caster, 
									 t_combat_creature const& target ) const;
	};

	inline t_spell_necromancy_ward::t_spell_necromancy_ward( t_battlefield& battlefield, t_spell spell )
									: t_mass_only_spell( battlefield, spell )
	{
	}

	t_combat_spell_registration<t_spell_necromancy_ward> const
		k_register_necromancy_ward( k_spell_sacred_ground );
};

double t_spell_necromancy_ward::get_spell_protection( t_combat_creature const& caster, 
													  t_combat_creature const& target ) const
{
	t_spell			spell;
	double			value;
	double			target_value;
	double			strongest_unaffected = 0.0;
	double			strongest_affected = 0.0;

	target_value = target.get_ai_value_per_hit() * target.get_total_hits();
	for (spell = t_spell(0); spell < k_spell_count; enum_incr( spell ))
	{
		if (!caster.can_cast( spell ))
			continue;
		value = caster.get_ai_estimated_value( spell );
		if (spell == k_spell_raise_skeletons || spell == k_spell_raise_ghost
			|| spell == k_spell_raise_vampires || spell == k_spell_animate_dead
			|| spell == k_spell_death_call)
		{
			if (strongest_affected < value)
				strongest_affected = value;
		}
		else
		{
			if (strongest_unaffected < value)
				strongest_unaffected = value;
		}
	}
	if (strongest_unaffected > strongest_affected)
		return 0.0;
	return strongest_affected - strongest_unaffected;
}

double t_spell_necromancy_ward::ai_weight( t_combat_creature const& target ) const
{
	if (target.get_salvageable_bodies() == 0)
		return 0.0;
	if (!spellcasting_is_worthwhile( m_battlefield, false ) || !spellcasting_is_worthwhile( m_battlefield, true ))
		return 0.0;
	if (get_approximate_battle_length( m_battlefield ) < 2)
		return 0.0;

	t_battlefield&						battlefield = m_battlefield;
	t_combat_creature_list::iterator	index = battlefield.creatures_begin();
	t_combat_creature_list::iterator	end = battlefield.creatures_end();
	double								value;
	double								dead_value;
	double								best_friendly = 0.0;
	double								best_enemy = 0.0;
	t_combat_creature const*			caster;

	dead_value = target.get_ai_value_per_hit() * target.get_salvageable_bodies() 
				* target.get_hit_points();
	for (; index != end; ++index)
	{
		caster = *index;
		if (caster->get_number() == 0)
			continue;
		if (caster->get_spell_points() <= 0)
			continue;
		if (!caster->can_cast( k_spell_raise_skeletons )
			&& !caster->can_cast( k_spell_raise_ghost )
			&& !caster->can_cast( k_spell_raise_vampires )
			&& !caster->can_cast( k_spell_animate_dead )
			&& !caster->can_cast( k_spell_death_call))
			continue;

		value = get_spell_protection( *caster, target );
		if (value > dead_value)
			value = dead_value;
		if (caster->belongs_to_defender() == m_caster->get_controller())
		{
			if (best_friendly < value)
				best_friendly = value;
		}
		else
		{
			if (best_enemy < value)
				best_enemy = value;
		}
	}
	return best_enemy - best_friendly;
}

double t_spell_necromancy_ward::get_cancel_weight( t_combat_creature const& target ) const
{
	return -ai_weight( target );
}

// ----------------------------------------------------------------
// life drain
// ----------------------------------------------------------------
namespace
{
	class t_life_drain : public t_mass_spell_base
	{
	public:
		t_life_drain( t_battlefield& battlefield, t_spell spell );

		virtual bool cast_on( t_combat_creature_ptr caster,
			                  t_combat_creature_ptr target, 
							  int power,
							  bool is_mirror );
		virtual int	 get_value( t_combat_creature* target ) const;
		virtual double	get_cancel_weight( t_combat_creature const& target ) const;
	protected:
		void receive_life( t_combat_creature_ptr caster, t_map_point_2d target, t_combat_action_message message );

		int m_total_gain;
	};
};

static t_combat_spell_registration<t_life_drain>
       k_register_life_drain( k_spell_life_drain );

// ----------------------------------------------------------------
// life drain
// ----------------------------------------------------------------
t_life_drain::t_life_drain( t_battlefield& battlefield, t_spell spell )
		    : t_mass_spell_base( battlefield, spell )
{
	m_total_gain = 0;
}


// ----------------------------------------------------------------
// life drain
// ----------------------------------------------------------------
bool t_life_drain::cast_on( t_combat_creature_ptr caster,
			                t_combat_creature_ptr target, 
							int power,
							bool is_mirror )
{
	int damage;

	damage = target->modify_spell_damage( caster, power, m_spell );
	if (damage == 0)
		return false;
	
	t_missile_handler handler;
	t_combat_action_message message( *caster, get_action_text( is_mirror ) );

	damage = target->add_damage( damage, caster, false, false, message );
	target->check_flinch( message );
	target->apply_damage( true );
	damage = damage / 2;
	handler = add_3rd_argument( bound_handler( *this, &t_life_drain::receive_life ), message );
	m_total_gain += damage;
	m_battlefield.launch_missile( k_missile_life_force, *target, 
		                           target->get_body_center(), caster->get_body_center(),
								   handler, message);
	m_battlefield.set_spell( this );
	return true;
}

// ----------------------------------------------------------------
// life drain
// ----------------------------------------------------------------
int t_life_drain::get_value( t_combat_creature* target ) const
{
	return 10;
}

double t_life_drain::get_cancel_weight( t_combat_creature const& target ) const
{
	return 0.0;
}

// ----------------------------------------------------------------
// life drain
// ----------------------------------------------------------------
void t_life_drain::receive_life( t_combat_creature_ptr caster, t_map_point_2d target, t_combat_action_message message )
{
	t_battlefield& battlefield = m_battlefield;

	if (m_total_gain > 0)
	{
		int power = 0;

		power = m_total_gain - m_caster->heal( m_total_gain );
		if (power > 0)
		{
			m_caster->set_spell( m_spell, message, power );
		}
		battlefield.add_text( *m_caster, format_string( "+%i", m_total_gain ), 
						      m_caster->get_message_color(), k_message_icon_heal );
		m_total_gain = 0;
	}
	battlefield.update_state();
}
